package com.jinrustar.shenjiyingcommon.common.util

import android.os.Handler

import android.os.Looper
import android.util.Log
import android.widget.ImageView
import com.squareup.okhttp.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLConnection


/**
 * Created by wei on 18-1-4.
 */
object OkHttpClientManager {


    private var mOkHttpClient: OkHttpClient
    private var mDelivery: Handler

    private val TAG = "OkHttpClientManager"

    init {
        mOkHttpClient = OkHttpClient()
        mDelivery = Handler(Looper.getMainLooper())
    }


    /**
     * 同步的Get请求
     *
     * @param url
     * @return Response
     */
    @Throws(IOException::class)
    private fun _getAsyn(url: String): Response {
        val request = Request.Builder()
                .url(url)
                .build()
        val call = mOkHttpClient.newCall(request)
        return call.execute()
    }

    /**
     * 同步的Get请求
     *
     * @param url
     * @return 字符串
     */
    @Throws(IOException::class)
    private fun _getAsString(url: String): String {
        val execute = _getAsyn(url)
        return execute.body().string()
    }


    /**
     * 异步的get请求
     *
     * @param url
     * @param callback
     */
    private fun _getAsyn(url: String, callback: StringCallback) {
        val request = Request.Builder()
                .url(url)
                .build()
        deliveryResult(callback, request)
    }


    /**
     * 同步的Post请求
     *
     * @param url
     * @param params post的参数
     * @return
     */
    @Throws(IOException::class)
    private fun _post(url: String, params: Array<Param>): Response {
        val request = buildPostRequest(url, params)
        return mOkHttpClient.newCall(request).execute()
    }


    /**
     * 同步的Post请求
     *
     * @param url
     * @param params post的参数
     * @return 字符串
     */
    @Throws(IOException::class)
    private fun _postAsString(url: String, params: Array<Param>): String {
        val response = _post(url, params)
        return response.body().string()
    }

    /**
     * 异步的post请求
     *
     * @param url
     * @param callback
     * @param params
     */
    private fun _postAsync(url: String, callback: StringCallback, params: Array<Param>) {
        val request = buildPostRequest(url, params)
        deliveryResult(callback, request)
    }

    /**
     * 同步基于post的文件上传
     *
     * @param params
     * @return
     */
    @Throws(IOException::class)
    private fun _post(url: String, files: Array<File>, fileKeys: Array<String>, params: Array<Param>): Response {
        val request = buildMultipartFormRequest(url, files, fileKeys, params)
        return mOkHttpClient.newCall(request).execute()
    }

    @Throws(IOException::class)
    private fun _post(url: String, file: File, fileKey: String): Response {
        val request = buildMultipartFormRequest(url, arrayOf<File>(file), arrayOf(fileKey), null)
        return mOkHttpClient.newCall(request).execute()
    }

    @Throws(IOException::class)
    private fun _post(url: String, file: File, fileKey: String, params: Array<Param>): Response {
        val request = buildMultipartFormRequest(url, arrayOf<File>(file), arrayOf(fileKey), params)
        return mOkHttpClient.newCall(request).execute()
    }

    /**
     * 异步基于post的文件上传
     *
     * @param url
     * @param callback
     * @param files
     * @param fileKeys
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun _postAsync(url: String, callback: StringCallback, files: Array<File>, fileKeys: Array<String>, params: Array<Param>) {
        val request = buildMultipartFormRequest(url, files, fileKeys, params)
        deliveryResult(callback, request)
    }

    /**
     * 异步基于post的文件上传，单文件不带参数上传
     *
     * @param url
     * @param callback
     * @param file
     * @param fileKey
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun _postAsync(url: String, callback: StringCallback, file: File, fileKey: String) {
        val request = buildMultipartFormRequest(url, arrayOf<File>(file), arrayOf(fileKey), null)
        deliveryResult(callback, request)
    }

    /**
     * 异步基于post的文件上传，单文件且携带其他form参数上传
     *
     * @param url
     * @param callback
     * @param file
     * @param fileKey
     * @param params
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun _postAsync(url: String, callback: StringCallback, file: File, fileKey: String, params: Array<Param>) {
        val request = buildMultipartFormRequest(url, arrayOf<File>(file), arrayOf(fileKey), params)
        deliveryResult(callback, request)
    }

    /**
     * 异步下载文件
     *
     * @param url
     * @param destFileDir 本地文件存储的文件夹
     * @param callback
     */
    private fun _downloadAsyn(url: String, destFileDir: String, callback: StringCallback) {
        val request = Request.Builder()
                .url(url)
                .build()
        val call = mOkHttpClient.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(request: Request, e: IOException) {
                sendFailedStringCallback(request, e, callback)
            }

            override fun onResponse(response: Response) {
                var inputStream: InputStream? = null
                val buf = ByteArray(2048)
                var len = 0
                var fos: FileOutputStream? = null
                try {
                    inputStream = response.body().byteStream()
                    val file = File(destFileDir, getFileName(url))
                    fos = FileOutputStream(file)
                    while (true) {
                        var len = inputStream!!.read(buf)
                        if (-1 == len) {
                            break
                        }
                        fos!!.write(buf, 0, len)
                    }
                    fos!!.flush()
                    //如果下载文件成功，第一个参数为文件的绝对路径
                    sendSuccessStringCallback(file.getAbsolutePath(), callback)
                } catch (e: IOException) {
                    sendFailedStringCallback(response.request(), e, callback)
                } finally {
                    try {
                        if (inputStream != null) inputStream!!.close()
                    } catch (e: IOException) {
                    }

                    try {
                        if (fos != null) fos!!.close()
                    } catch (e: IOException) {
                    }

                }

            }
        })
    }

    private fun getFileName(path: String): String {
        val separatorIndex = path.lastIndexOf("/")
        return if (separatorIndex < 0) path else path.substring(separatorIndex + 1, path.length)
    }


    private fun setErrorResId(view: ImageView, errorResId: Int) {
        mDelivery.post(Runnable { view.setImageResource(errorResId) })
    }


    //*************对外公布的方法************


    @Throws(IOException::class)
    fun getAsyn(url: String): Response {
        return _getAsyn(url)
    }


    @Throws(IOException::class)
    fun getAsString(url: String): String {
        return _getAsString(url)
    }

    fun getAsyn(url: String, callback: StringCallback) {
        _getAsyn(url, callback)
    }

    @Throws(IOException::class)
    fun post(url: String, params: Array<Param>): Response {
        return _post(url, params)
    }

    @Throws(IOException::class)
    fun postAsString(url: String, params: Array<Param>): String {
        return _postAsString(url, params)
    }

    fun postAsync(url: String, callback: StringCallback, params: Array<Param>) {
        _postAsync(url, callback, params)
    }





    @Throws(IOException::class)
    fun post(url: String, files: Array<File>, fileKeys: Array<String>, params: Array<Param>): Response {
        return _post(url, files, fileKeys, params)
    }

    @Throws(IOException::class)
    fun post(url: String, file: File, fileKey: String): Response {
        return _post(url, file, fileKey)
    }

    @Throws(IOException::class)
    fun post(url: String, file: File, fileKey: String, params: Array<Param>): Response {
        return _post(url, file, fileKey, params)
    }

    @Throws(IOException::class)
    fun postAsync(url: String, callback: StringCallback, files: Array<File>, fileKeys: Array<String>, params: Array<Param>) {
        _postAsync(url, callback, files, fileKeys, params)
    }


    @Throws(IOException::class)
    fun postAsync(url: String, callback: StringCallback, file: File, fileKey: String) {
        _postAsync(url, callback, file, fileKey)
    }


    @Throws(IOException::class)
    fun postAsync(url: String, callback: StringCallback, file: File, fileKey: String, params: Array<Param>) {
        _postAsync(url, callback, file, fileKey, params)
    }


    fun downloadAsyn(url: String, destDir: String, callback: StringCallback) {
        _downloadAsyn(url, destDir, callback)
    }

    //****************************


    private fun buildMultipartFormRequest(url: String, files: Array<File>?,
                                          fileKeys: Array<String>, params: Array<Param>?): Request {
        var params = params
        params = validateParam(params)

        val builder = MultipartBuilder()
                .type(MultipartBuilder.FORM)

        for (param in params!!) {
            builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.key + "\""),
                    RequestBody.create(null, param.value))
        }
        if (files != null) {
            var fileBody: RequestBody? = null
            for (i in files.indices) {
                val file = files[i]
                val fileName = file.getName()
                fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file)
                //TODO 根据文件名设置contentType
                builder.addPart(Headers.of("Content-Disposition",
                        "form-data; name=\"" + fileKeys[i] + "\"; filename=\"" + fileName + "\""),
                        fileBody!!)
            }
        }

        val requestBody = builder.build()
        return Request.Builder()
                .url(url)
                .post(requestBody)
                .build()
    }

    private fun guessMimeType(path: String): String {
        val fileNameMap = URLConnection.getFileNameMap()
        var contentTypeFor = fileNameMap.getContentTypeFor(path)
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream"
        }
        return contentTypeFor
    }


    private fun validateParam(params: Array<Param>?): Array<Param>? {
        return params
    }

    private fun map2Params(params: Map<String, String>?): Array<Param?> {
        if (params == null) return arrayOfNulls(0)
        val size = params.size
        val res = arrayOfNulls<Param>(size)
        val entries = params.entries
        var i = 0
        for ((key, value) in entries) {
            res[i++] = Param(key, value)
        }
        return res
    }

    private fun deliveryResult(callback: StringCallback, request: Request) {
        mOkHttpClient.newCall(request).enqueue(object : Callback {
            override fun onFailure(request: Request, e: IOException) {
                Log.e(TAG, request.toString())
                Log.e(TAG, e.message)
                sendFailedStringCallback(request, e, callback)
            }

            override fun onResponse(response: Response) {
                val string = response.body().string()
                Log.e(TAG, string)
                sendSuccessStringCallback(string, callback)/*2018.1.5 android 报错*/
                /*try {
                } catch (e: IOException) {
                    sendFailedStringCallback(response.request(), e, callback)
                }*/
            }
        })
    }

    private fun sendFailedStringCallback(request: Request, e: IOException, callback: StringCallback?) {
        mDelivery.post({
            callback?.onFailure(request, e)
        })
    }

    private fun sendSuccessStringCallback(string: String, callback: StringCallback?) {
        mDelivery.post({
            callback?.onResponse(string)
        })
    }

    private fun buildPostRequest(url: String, params: Array<Param>?): Request {
        var params = params
        if (params == null) {
            params = arrayOf()
        }
        val builder = FormEncodingBuilder()
        for (param in params) {
            builder.add(param.key, param.value)
        }
        val requestBody = builder.build()
        return Request.Builder()
                .url(url)
                .post(requestBody)
                .build()
    }


    interface StringCallback {
        fun onFailure(request: Request, e: IOException)

        fun onResponse(response: String)
    }

    data class Param(var key: String, var value: String)





}